package com.utils;

import android.app.Activity;
import android.bluetooth.*;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.os.Handler;
import android.util.Log;
import java.util.*;

public class BlueToothUtil {
    private static final String TAG = BlueToothUtil.class.getSimpleName();
    private static BlueToothUtil mInstance;
    private Activity mContext;
    private String mAddress;
    private String mName;
    private int isConnected = 1;      //0:连接中    1：未连接    2：已连接
    private BluetoothGatt mBluetoothGatt;
    private BleConnectCallback mConnectIml;
    private BleNotifyCallback mNotifyIml;
    private Handler mHandler;
    private Timer mTimer;
    private BluetoothLeScanner bluetoothLeScanner;
    private BluetoothAdapter bluetoothAdapter;

    public static int BT_CONNECT_STATUS = 100;     //地锁未连接
    private static String serviceUUID = "";
    private static String readNotifyUUID = "";
    private static String descNotifyUUID = "";
    private static String writeNotifyUUID = "";

    private int TIME_INTERVAL = 8 * 1000;        //扫描时间间隔  单位：S（秒）

    public static BlueToothUtil getInstance() {
        if (mInstance == null) {
            mInstance = new BlueToothUtil();
        }
        return mInstance;
    }

    BlueToothUtil() {
        mHandler = new Handler();
    }

    /**
     * 扫描并连接
     */
    public void scanConnect(Activity context, String scanName, BleConnectCallback connectIml) {
        this.mContext = context;
        this.mName = scanName;
        this.mConnectIml = connectIml;
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();

        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (isConnected == 1 || isConnected == 0) {
                    bluetoothLeScanner.stopScan(mScanIml);
                    mContext.runOnUiThread(() -> mConnectIml.onConnectFail(0));
                    mTimer.cancel();
                    mTimer = null;
                }
            }
        }, TIME_INTERVAL);
        bluetoothLeScanner.startScan(mScanIml);
    }

    /**
     * 扫描回调
     */
    ScanCallback mScanIml = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            String name = result.getScanRecord().getDeviceName();
            if (name == null) {
                name = "空";
            }

            if (name != null && name.equals(mName) && isConnected == 1) {
                isConnected = 0;
                closeConn();
                mAddress = result.getDevice().getAddress();
                mContext.runOnUiThread(() -> mConnectIml.onStartConnect());
                mBluetoothGatt = result.getDevice().connectGatt(mContext, false, mBluetoothGattCallback);
                Log.i(TAG, "与[" + result.getDevice() + "]开始连接............");
                bluetoothLeScanner.stopScan(this);
            }
        }
    };

    /**
     * 根据mac地址直接连接
     */
    public void connectBt(Activity context, String address, BleConnectCallback connectIml) {
        this.mContext = context;
        this.mAddress = address;
        this.mConnectIml = connectIml;
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothDevice mDevice = bluetoothAdapter.getRemoteDevice(address);
        mConnectIml.onStartConnect();
        mBluetoothGatt = mDevice.connectGatt(context, false, mBluetoothGattCallback);
    }

    /**
     * 设置通知Characteristic变化会回调->onCharacteristicChanged()
     * 获取不到GattService时断开重连
     */
    public static int UUID_CONFIG_CODE = 1001;    //UUID错误

    public void setNotify() {
        try {
            BluetoothGattService service = getGattService(UUID.fromString(serviceUUID));
            if (service != null) {
                // 设置Characteristic通知
                BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(readNotifyUUID));
                mBluetoothGatt.setCharacteristicNotification(characteristic, true);

                if (characteristic.getDescriptors().size() != 0) {
                    // 向Characteristic的Descriptor属性写入通知开关，使蓝牙设备主动向手机发送数据
                    BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
                    //BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(mDescNotifyUUID));
                    // descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);//和通知类似,但服务端不主动发数据,只指示客户端读取数据
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    mBluetoothGatt.writeDescriptor(descriptor);
                } else {
                    mContext.runOnUiThread(() -> mConnectIml.onConnectFail(UUID_CONFIG_CODE));
                }
            } else {
                mContext.runOnUiThread(() -> mConnectIml.onConnectFail(UUID_CONFIG_CODE));
            }
        } catch (Exception e) {
            mContext.runOnUiThread(() -> mConnectIml.onConnectFail(UUID_CONFIG_CODE));
        }
    }

    /**
     * 注意：连续频繁读写数据容易失败，读写操作间隔最好200ms以上，或等待上次回调完成后再进行下次读写操作！
     * 读取数据成功会回调->onCharacteristicChanged()
     */
    public void read() {
        BluetoothGattService service = getGattService(UUID.fromString(serviceUUID));
        if (service != null) {
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(readNotifyUUID));
            mBluetoothGatt.readCharacteristic(characteristic);
        }
    }

    /**
     * 注意：连续频繁读写数据容易失败，读写操作间隔最好200ms以上，或等待上次回调完成后再进行下次读写操作！
     * 写入数据成功会回调->onCharacteristicWrite()
     * isConnected  0:连接中    1：未连接    2：已连接
     */
    public void write(byte[] byteArr, BleNotifyCallback notifyIml) {
        try {
            this.mNotifyIml = notifyIml;

            if (isConnected == 1) {
                mNotifyIml.onNotifyFailure(BT_CONNECT_STATUS);
                return;
            }

            BluetoothGattService service = getGattService(UUID.fromString(serviceUUID));
            if (service != null) {
                BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(writeNotifyUUID));
                characteristic.setValue(byteArr); //单次最多20个字节
                mBluetoothGatt.writeCharacteristic(characteristic);
            } else {
                mNotifyIml.onNotifyFailure(0);
            }
        } catch (NullPointerException e) {
            mNotifyIml.onNotifyFailure(0);
        }
    }

    /**
     * 获取Gatt服务
     *
     * @param uuid
     * @return
     */
    private int MAX_FIND_UUID_COUNT = 10;        //寻找uuid的最大次数
    private int currentFindCount = 0;           //当前的寻找次数

    private BluetoothGattService getGattService(UUID uuid) {
        BluetoothGattService mBtGattService = null;
        currentFindCount = 0;

        while (true) {
            if (mBtGattService == null) {
                try {
                    if (currentFindCount < MAX_FIND_UUID_COUNT) {
                        currentFindCount = currentFindCount + 1;
                        Thread.sleep(100);
                        mBtGattService = mBluetoothGatt.getService(uuid);
                    } else {
                        return null;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                return mBtGattService;
            }
        }
    }

    /**
     * 与服务端连接的Callback
     * STATE_DISCONNECTED = 0;      //断开连接
     * STATE_CONNECTING = 1;        //正在连接状态
     * STATE_CONNECTED = 2;         //连接状态
     * STATE_DISCONNECTING = 3;     //正在断开连接
     */
    public BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            BluetoothDevice dev = gatt.getDevice();
            Log.i(TAG, String.format("onConnectionStateChange:%s,%s,%s,%s", dev.getName(), dev.getAddress(), status, newState));

            if (status == 133 && newState == BluetoothGatt.STATE_DISCONNECTED) {
                isConnected = 1;
                closeConn();
                mContext.runOnUiThread(() -> mConnectIml.onConnectFail(0));
            } else if (BluetoothGatt.STATE_CONNECTED == newState) {
                gatt.discoverServices();
                mHandler.postDelayed(() -> setNotify(), 500);
            } else if (BluetoothGatt.STATE_DISCONNECTED == newState) {
                isConnected = 1;
                closeConn();
                mContext.runOnUiThread(() -> mConnectIml.onDisConnected());
            }
            Log.i(TAG, String.format(status == 0 ? (newState == 2 ? "与[%s]连接成功" : "与[%s]连接断开") : ("与[%s]连接出错,错误码:" + status), dev));
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.i(TAG, "onServicesDiscovered");
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            UUID uuid = characteristic.getUuid();
            String valueStr = new String(characteristic.getValue());
            Log.i(TAG, String.format("读取onCharacteristicRead:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            UUID uuid = characteristic.getUuid();
            String valueStr = new String(characteristic.getValue());
            Log.i(TAG, String.format("写入onCharacteristicWrite:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            mContext.runOnUiThread(() -> mNotifyIml.onNotifySuccess());
            if (characteristic.getValue() != null) {
                isConnected = 2;
                mContext.runOnUiThread(() -> mNotifyIml.onCharacteristicChanged(characteristic.getValue()));
            }
            Log.i(TAG, String.format("通知onCharacteristicChanged:%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress()));
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            UUID uuid = descriptor.getUuid();
            String valueStr = Arrays.toString(descriptor.getValue());
            Log.i(TAG, String.format("读取onDescriptorRead:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            UUID uuid = descriptor.getUuid();
            String valueStr = Arrays.toString(descriptor.getValue());
            Log.i(TAG, String.format("写入onDescriptorWrite:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
            mContext.runOnUiThread(() -> mConnectIml.onNotifySuccess());
        }
    };

    public BlueToothUtil setServiceUUID(String mServiceUUID) {
        serviceUUID = mServiceUUID;
        return this;
    }

    public BlueToothUtil setReadNotifyUUID(String mReadNotifyUUID) {
        readNotifyUUID = mReadNotifyUUID;
        return this;
    }

    public BlueToothUtil setDescNotifyUUID(String mDescNotifyUUID) {
        descNotifyUUID = mDescNotifyUUID;
        return this;
    }

    public BlueToothUtil setWriteNotifyUUID(String mWriteNotifyUUID) {
        writeNotifyUUID = mWriteNotifyUUID;
        return this;
    }

    /**
     * 蓝牙连接回调
     */
    public interface BleConnectCallback {
        void onStartConnect();

        void onConnectFail(int errorCode);

        void onConnectSuccess();

        void onDisConnected();

        void onNotifySuccess();
    }

    /**
     * 蓝牙通知
     */
    public interface BleNotifyCallback {
        void onNotifySuccess();

        void onNotifyFailure(int status);

        void onCharacteristicChanged(byte[] byteArr);
    }

    /**
     * BLE中心设备连接外围设备的数量有限(大概2~7个)，在建立新连接之前必须释放旧连接资源，否则容易出现连接错误133
     */
    public void closeConn() {
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
        }
    }

    public void destoryConn() {
        closeConn();
        if (mInstance != null) {
            mInstance = null;
        }
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
    }
}
